﻿using System;
using System.Linq.Expressions;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;

namespace KelonProud.Mvc.Controls
{
    public  class KelonProudControlHelper<TModel>
    {
        public  ModelExplorer GetModelExplorer<TResult>( IHtmlHelper helper,Expression<Func<TModel, TResult>> expression)
        {
            if (expression == null)
            {
                throw new ArgumentNullException(nameof(expression));
            }

            var modelExplorer =
                ExpressionMetadataProvider.FromLambdaExpression(expression, GetViewData(helper), helper.MetadataProvider);

            return modelExplorer;
        }

        private  ViewDataDictionary<TModel> GetViewData( IHtmlHelper helper)
        {
            var viewContext = helper.ViewContext;

            if (viewContext == null)
            {
                throw new ArgumentNullException(nameof(viewContext));
            }

            var viewData = viewContext.ViewData as ViewDataDictionary<TModel>;

            if (viewData == null)
            {
                // The view data that we have at this point might be of a more derived type than the one defined at compile time.
                // For example ViewDataDictionary<Derived> where our TModel is Base and Derived : Base.
                // This can't happen for regular views, but it can happen in razor pages if someone modified the model type through
                // the page application model.
                // In that case, we check if the type of the current view data, 'ViewDataDictionary<TRuntime>' is "covariant" with the
                // one defined at compile time 'ViewDataDictionary<TCompile>'
                var runtimeType = viewContext.ViewData.ModelMetadata.ModelType;
                if (runtimeType != null && typeof(TModel) != runtimeType && typeof(TModel).IsAssignableFrom(runtimeType))
                {
                    viewData = new ViewDataDictionary<TModel>(viewContext.ViewData, viewContext.ViewData.Model);
                }
            }

            return viewData;
        }
    }
}
